﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using DomainDriver.DomainModeling.DataProviders;
using DomainDriver.DomainModeling.DomainObjects;
using DomainDriver.DomainModeling.Queries;
using DomainDriver.DomainModeling.StorageManagers.DataSetStorage;
using DomainDriver.DomainModeling.StorageManagers.EntityFrameworkStorage;
using ObjectRow = DomainDriver.DomainModeling.Example.ExampleModelDataSet.DummyDomainObjectsRow;
using RelationRow = DomainDriver.DomainModeling.Example.ExampleModelDataSet.DummyDomainObjectRelationsRow;

namespace DomainDriver.DomainModeling.Example.DomainObjects
{
    public partial class DummyDomainObject : IEfEntity<Guid>, IEfAggregateRoot<DummyDomainObject>, IDataSetSerializable<DataSet, DataRow>, IDataSetSerializable<ExampleModelDataSet, DataRow>, IDataSetSerializable<ExampleModelDataSet, ObjectRow>
    {
        #region Entity Framework CTP5 Serialization Code

        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public Guid EF_Key
        {
            get { return m_Key; }
            set { m_Key = value; }
        }

        [NotMapped]
        public ICollection<DummyDomainObjectRelation> EF_Children
        {
            get { return m_Children.Values; }
            set
            {
                m_Children.Clear();
                foreach (DummyDomainObjectRelation relation in value)
                {
                    m_Children.Add(relation.ChildId, relation);
                }
            }
        }

        ICollection<QuerySpecification> IEfAggregateRoot<DummyDomainObject>.OverriddenQueries
        {
            get
            {
                List<QuerySpecification> specifications = new List<QuerySpecification>();
                specifications.Add(new QuerySpecification(typeof(ContainsQuery<DummyDomainObject>), new string[] { "Children" }));
                return specifications;
            }
        }

        bool IEfAggregateRoot<DummyDomainObject>.SearchNestedAggregateValues(DbContext context, IParameterizedQuery<DummyDomainObject> query)
        {
            IDictionary<string, object> parameterValues = query.ParameterValues;
            int containsParameterCount = 1;
            string containsParameterName = "Children";

            if ((query is ContainsQuery<DummyDomainObject>) && (parameterValues.Count == containsParameterCount) && (parameterValues.ContainsKey(containsParameterName)))
            {
                Guid childId = (Guid)parameterValues[containsParameterName];

                Expression<Func<DummyDomainObjectRelation, bool>> childQueryExpression = ((DummyDomainObjectRelation rel) => ((rel.ParentId == this.Key) && (rel.ChildId == childId)));
                IEnumerable<DummyDomainObjectRelation> childQueryResults = context.Set<DummyDomainObjectRelation>().Where<DummyDomainObjectRelation>(childQueryExpression.Compile());
                bool containsChild = (childQueryResults.Count<DummyDomainObjectRelation>() > 0);
                return containsChild;
            }

            throw new NotImplementedException();
        }

        void IEfAggregateRoot<DummyDomainObject>.ReadNestedAggregateValues(DbContext context)
        {
            IQueryable<DummyDomainObjectRelation> relations = context.Set<DummyDomainObjectRelation>().Where<DummyDomainObjectRelation>((DummyDomainObjectRelation rel) => rel.ParentId == this.Key);
            List<DummyDomainObjectRelation> relationList = new List<DummyDomainObjectRelation>(relations);
            this.EF_Children = relationList;
        }

        void IEfAggregateRoot<DummyDomainObject>.AddNestedAggregateValues(DbContext context)
        {
            Expression<Func<ICollection<DummyDomainObjectRelation>>> nestedObjectsGetterExpression = () => this.EF_Children;
            EfAggregateUtilities.AddNestedValues<DummyDomainObjectRelation>(context, nestedObjectsGetterExpression);
        }

        void IEfAggregateRoot<DummyDomainObject>.UpdateNestedAggregateValues(DbContext context, DummyDomainObject originalAggregateRoot)
        {
            Expression<Func<DummyDomainObjectRelation, Guid>> keyGetterExpression = (DummyDomainObjectRelation nestedObj) => nestedObj.ChildId;
            Expression<Func<DummyDomainObjectRelation, DummyDomainObjectRelation, bool>> nestedObjectsMatchExpression = (DummyDomainObjectRelation obj1, DummyDomainObjectRelation obj2) => ((obj1.ParentId == obj2.ParentId) && (obj1.ChildId == obj2.ChildId));
            Expression<Func<DummyDomainObject, IDictionary<Guid, DummyDomainObjectRelation>>> dictionaryGetterExpression = (DummyDomainObject agg) => agg.m_Children;
            EfAggregateUtilities.UpdateNestedValues<DummyDomainObject, Guid, DummyDomainObjectRelation>(context, this, originalAggregateRoot, keyGetterExpression, nestedObjectsMatchExpression, dictionaryGetterExpression);
        }

        void IEfAggregateRoot<DummyDomainObject>.DeleteNestedAggregateValues(DbContext context)
        {
            Expression<Func<DummyDomainObjectRelation, bool>> deleteQueryExpression = ((DummyDomainObjectRelation rel) => (rel.ParentId == this.Key));
            EfAggregateUtilities.DeleteNestedValues(context, deleteQueryExpression);
        }

        #endregion

        #region DataSet Serialization Methods

        public void Serialize(DataSet dataSource, DataRow objectRow)
        {
            string keyColumnName = ExampleModelDataSet.SchemaHelper.DummyDomainObjects.DummyDomainObjectIdColumn.ColumnName;
            string indexColumnName = ExampleModelDataSet.SchemaHelper.DummyDomainObjects.IndexColumn.ColumnName;
            string nameColumnName = ExampleModelDataSet.SchemaHelper.DummyDomainObjects.NameColumn.ColumnName;
            string parentIdColumnName = ExampleModelDataSet.SchemaHelper.DummyDomainObjects.ParentDummyDomainObjectIdColumn.ColumnName;

            objectRow[keyColumnName] = m_Key;
            objectRow[indexColumnName] = m_Index;
            objectRow[nameColumnName] = m_Name;
            DataSetSerializationUtilities.SerializeNullableField<Guid>(m_Parent, objectRow, ExampleModelDataSet.SchemaHelper.DummyDomainObjects.ParentDummyDomainObjectIdColumn);

            string subTableName = ExampleModelDataSet.SchemaHelper.DummyDomainObjectRelations.TableName;
            string subParentIdColumnName = ExampleModelDataSet.SchemaHelper.DummyDomainObjectRelations.ParentDummyDomainObjectIdColumn.ColumnName;
            string subChildIdColumnName = ExampleModelDataSet.SchemaHelper.DummyDomainObjectRelations.ChildDummyDomainObjectIdColumn.ColumnName;
            DataTable relationTable = dataSource.Tables[subTableName];

            foreach (DummyDomainObjectRelation relation in m_Children.Values)
            {
                DataRow relationRow = relationTable.NewRow();
                relationRow[subParentIdColumnName] = relation.ParentId;
                relationRow[subChildIdColumnName] = relation.ChildId;
                relationTable.Rows.Add(relationRow);
            }
        }

        public void Deserialize(DataSet dataSource, DataRow objectRow)
        {
            string keyColumnName = ExampleModelDataSet.SchemaHelper.DummyDomainObjects.DummyDomainObjectIdColumn.ColumnName;
            string indexColumnName = ExampleModelDataSet.SchemaHelper.DummyDomainObjects.IndexColumn.ColumnName;
            string nameColumnName = ExampleModelDataSet.SchemaHelper.DummyDomainObjects.NameColumn.ColumnName;
            string parentIdColumnName = ExampleModelDataSet.SchemaHelper.DummyDomainObjects.ParentDummyDomainObjectIdColumn.ColumnName;

            m_Key = objectRow.Field<Guid>(keyColumnName);
            m_Index = objectRow.Field<int>(indexColumnName);
            m_Name = objectRow.Field<string>(nameColumnName);
            DataSetSerializationUtilities.DeserializeNullableField<Guid>(out m_Parent, objectRow, ExampleModelDataSet.SchemaHelper.DummyDomainObjects.ParentDummyDomainObjectIdColumn);

            string subTableName = ExampleModelDataSet.SchemaHelper.DummyDomainObjectRelations.TableName;
            string subParentIdColumnName = ExampleModelDataSet.SchemaHelper.DummyDomainObjectRelations.ParentDummyDomainObjectIdColumn.ColumnName;
            string subChildIdColumnName = ExampleModelDataSet.SchemaHelper.DummyDomainObjectRelations.ChildDummyDomainObjectIdColumn.ColumnName;
            DataTable relationTable = dataSource.Tables[subTableName];
            string selectStmt = string.Format("{0} = '{1}'", subParentIdColumnName, m_Key.ToString());

            foreach (DataRow relationRow in relationTable.Select(selectStmt))
            {
                if (relationRow.IsNull(subParentIdColumnName))
                { throw new ApplicationException(); }
                if (relationRow.Field<Guid>(subParentIdColumnName) != m_Key)
                { throw new ApplicationException(); }

                Guid childId = relationRow.Field<Guid>(subChildIdColumnName);
                m_Children.Add(childId, new DummyDomainObjectRelation(m_Key, childId));
            }
        }

        #endregion

        #region ExampleModelDataSet Serialization Methods

        public string ObjectTableName
        {
            get { return ExampleModelDataSet.SchemaHelper.DummyDomainObjects.TableName; }
        }

        public string KeyColumnName
        {
            get { return ExampleModelDataSet.SchemaHelper.DummyDomainObjects.DummyDomainObjectIdColumn.ColumnName; }
        }

        public void Serialize(ExampleModelDataSet dataSource, DataRow objectRow)
        {
            Serialize(dataSource, objectRow as ObjectRow);
        }

        public void Serialize(ExampleModelDataSet dataSource, ObjectRow objectRow)
        {
            objectRow.DummyDomainObjectId = m_Key;
            objectRow.Index = m_Index;
            objectRow.Name = m_Name;

            DataSetSerializationUtilities.SerializeNullableField<Guid>(m_Parent, objectRow, ExampleModelDataSet.SchemaHelper.DummyDomainObjects.ParentDummyDomainObjectIdColumn);

            foreach (DummyDomainObjectRelation relation in m_Children.Values)
            {
                RelationRow relationRow = dataSource.DummyDomainObjectRelations.NewDummyDomainObjectRelationsRow();
                relationRow.ParentDummyDomainObjectId = relation.ParentId;
                relationRow.ChildDummyDomainObjectId = relation.ChildId;
                dataSource.DummyDomainObjectRelations.AddDummyDomainObjectRelationsRow(relationRow);
            }
        }

        public void Deserialize(ExampleModelDataSet dataSource, DataRow objectRow)
        {
            Deserialize(dataSource, objectRow as ObjectRow);
        }

        public void Deserialize(ExampleModelDataSet dataSource, ObjectRow objectRow)
        {
            m_Key = objectRow.DummyDomainObjectId;
            m_Index = objectRow.Index;
            m_Name = objectRow.Name;

            DataSetSerializationUtilities.DeserializeNullableField<Guid>(out m_Parent, objectRow, ExampleModelDataSet.SchemaHelper.DummyDomainObjects.ParentDummyDomainObjectIdColumn);

            foreach (RelationRow relationRow in objectRow.GetDummyDomainObjectRelationsRowsByDummyDomainObjects_DummyDomainObjects_Children_Parent())
            {
                if (relationRow.ParentDummyDomainObjectId != m_Key)
                { throw new ApplicationException(); }

                Guid childId = relationRow.ChildDummyDomainObjectId;
                m_Children.Add(childId, new DummyDomainObjectRelation(m_Key, childId));
            }
        }

        #endregion
    }
}